package com.future.util;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;

/**
 * Description:
 *
 * @author weiruibai.vendor
 * Date: 2022/9/6 17:51
 */
@Service
public class RedissionDistributedLock<T> {

    private static final Logger LOGGER = LoggerFactory.getLogger(RedissionDistributedLock.class);

    private static final String PRE = "distribute_";

    private static final Integer EXPIRE_TIME = 3; // 秒

    @Autowired
    private RedissonClient redisson;

    /**
     * get distribute lok
     * <p>
     * "if (redis.call('exists', KEYS[1]) == 0) then " +
     * "redis.call('hset', KEYS[1], ARGV[2], 1); " +
     * "redis.call('pexpire', KEYS[1], ARGV[1]); " +
     * "return nil; " +
     * "end; " +
     * "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
     * "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
     * "redis.call('pexpire', KEYS[1], ARGV[1]); " +
     * "return nil; " +
     * "end; " +
     *
     * @param key cache key
     * @return is lock success
     */
    public T tryLok(String key, Supplier<T> supplier) {
        boolean isLockSuccess = false;
        RLock lock = null;
        T data = null;
        key = PRE + key;
        try {
            lock = redisson.getLock(key);
            isLockSuccess = lock.tryLock(2, EXPIRE_TIME, TimeUnit.SECONDS);
            if (isLockSuccess) {
                data = supplier.get();
                LOGGER.info("{} success get distribute lock. key: {}", Thread.currentThread(), key);
            } else {
                LOGGER.info("{} fail get distribute lock. key: {}", Thread.currentThread(), key);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (lock != null) {
                lock.unlock();
                LOGGER.info("{} release lock: {}", Thread.currentThread(), key);
            }
        }
        return data;
    }
}
